#pragma once
namespace ne{
enum EComponentMessages{
	NE_MSG_NO_MSG = 0,
	NE_MSG_REMOVE_COLLIDER,
	NE_MSG_SET_POSITION,
	NE_MSG_SET_ORIENTATION,
	NE_MSG_GET_OGRE_ENTITY,
	NE_MSG_MESH_COMPONENT_DESTROYED,
	NE_MSG_SIZE
};

struct CEvent{
	CEvent():m_type(NE_MSG_NO_MSG){}
	CEvent(int type):m_type(type){}
	int m_type;
};

struct CEventVec3:CEvent{
	CEventVec3():m_vec3(Ogre::Vector3::ZERO){}
	CEventVec3(int type, const Ogre::Vector3& vec3):CEvent(type),m_vec3(vec3){}
	Ogre::Vector3 m_vec3;
};

struct CEventQuat:CEvent{
	CEventQuat():m_q(Ogre::Quaternion::IDENTITY){}
	CEventQuat(int type, const Ogre::Quaternion& q):CEvent(type),m_q(q){}
	Ogre::Quaternion m_q;
};

struct CEventEntity:CEvent{
	CEventEntity(int p_type,Ogre::Entity** p_entity):CEvent(p_type),m_entity(p_entity){}
	Ogre::Entity** m_entity;
};

enum ComponentFlagTypes{
	COMPONENT_CONSTRAINT =				0x000001,
	COMPONENT_CONSTRAINT_HINGE =		0x000002,
	COMPONENT_CONSTRAINT_POINT2POINT =	0x000004,
	COMPONENT_CONSTRAINT_6DOF =			0x000008,
	COMPONENT_CONSTRAINT_SLIDER =		0x000010,
	COMPONENT_COLLIDER =				0x000020,
	COMPONENT_COLLIDER_BOX =			0x000040,
	COMPONENT_COLLIDER_SPHERE =			0x000080,
	COMPONENT_COLLIDER_CAPSULE =		0x000100,
	COMPONENT_COLLIDER_STATIC_MESH =	0x000200,
	COMPONENT_RIGIDBODY =				0x000400,
	COMPONENT_MESH =					0x000800,
	COMPONENT_LIGHT =					0x001000,
	COMPONENT_ANIMATION =				0x002000,
	COMPONENT_CHARACTER_CONTROLLER =	0x004000,
	COMPONENT_CAMERA =					0x008000,
	COMPONENT_FIRST_PERSON_CAMERA =		0x010000,
	COMPONENT_THIRD_PERSON_CAMERA =		0x020000,
};

class GameObject;
class Component {
public:
	Component():m_type(0),m_owner(nullptr){}
	virtual ~Component(void){}
	virtual void Shut(){}
	int GetType() const { return m_type; }	// returns the type of component, eg: RIGIDBODY, MESH_RENDER, LIGHT etc. This is a flag so use bitwise & operator to check for type
	GameObject* GetOwner() const {return m_owner;}
	void SetOwner(GameObject* p_owner){m_owner=p_owner;}
	virtual void Init(){}
	virtual luabind::object GetAsLua(lua_State* L)=0;
	const Ogre::String& GetName()const{return m_name;}
	void SetName(const Ogre::String& name){m_name=name;}

protected:
	int m_type;
	GameObject* m_owner;
	Ogre::String m_name;
};

class GameTime;
class IComponentUpdate {
public:
	IComponentUpdate(){s_updates.push_front(this);m_it=s_updates.begin();}
	virtual ~IComponentUpdate(){s_updates.erase(m_it);}
	virtual void Update(GameTime* gameTime) = 0;
	static void UpdateAllComponents(GameTime* gameTime){
		if (!s_updates.empty()){
			for (auto it = s_updates.begin(); it != s_updates.end(); it++){
				(*it)->Update(gameTime);
			}
		}
	}
private:
	std::list<IComponentUpdate*>::iterator m_it;
	static std::list<IComponentUpdate*> s_updates;
};

//Late update is performed after physics simulation
class IComponentLateUpdate{
public:
	IComponentLateUpdate(){s_lateUpdates.push_front(this);m_it=s_lateUpdates.begin();}
	virtual ~IComponentLateUpdate(){s_lateUpdates.erase(m_it);}
	virtual void LateUpdate(GameTime* gameTime)=0;
	static void LateUpdateAllComponent(GameTime* gameTime){
		if (!s_lateUpdates.empty()){
			for (auto it = s_lateUpdates.begin(); it != s_lateUpdates.end(); it++){
				(*it)->LateUpdate(gameTime);
			}
		}
	}
private:
	std::list<IComponentLateUpdate*>::iterator m_it;
	static std::list<IComponentLateUpdate*> s_lateUpdates;
};

class IComponentObserver{
public:
	IComponentObserver(void){}
	virtual ~IComponentObserver(void){}
	virtual void Notify(CEvent* msg)=0;
};

class IComponentCollision{
public:
	IComponentCollision(void){}
	~IComponentCollision(void){}
	virtual void SetInContact(bool value)=0;
	virtual bool IsInContact()const=0;
};

struct RigidBodyDef{
	RigidBodyDef(void) : 
		mass(1.0f), 
		linearDamping(0.0f), 
		angularDamping(0.05f),
		isKinematic(false),
		gravity(btVector3(0.0f,-9.8f,0.0f)),
		linearFactor(btVector3(0.0f,0.0f,0.0f)), 
		angularFactor(btVector3(0.0f,0.0f,0.0f)){}
	float mass;
	float linearDamping;
	float angularDamping;
	bool isKinematic;
	btVector3 gravity;
	btVector3 linearFactor;
	btVector3 angularFactor;
};

struct MeshRenderDef{
	MeshRenderDef(void) : filename(Ogre::StringUtil::BLANK){}
	Ogre::String filename;
};

struct ColliderDef {
	ColliderDef(void) : positionOffset(btVector3(0,0,0)), rotationOffset(btQuaternion(0,0,0,1)){}
	btVector3 positionOffset;
	btQuaternion rotationOffset;
};

struct BoxColliderDef : public ColliderDef {
	BoxColliderDef(void) : x(0.0f), y(0.0f), z(0.0f) {}
	float x;
	float y;
	float z;
};

struct SphereColliderDef : public ColliderDef {
	SphereColliderDef(void) : radius(0.0f){}
	float radius;
};

struct CameraDef {
	CameraDef(void) : position(Ogre::Vector3::ZERO){}
	Ogre::String cameraName;
	Ogre::Vector3 position;
	float farClipDistance;
	float nearClipDistance;
	Ogre::ColourValue backgroundColor;
};
}